package eu.europeana.cloud.service.mcs.persistent.swift; import com.google.common.io.BaseEncoding; import com.google.common.io.ByteStreams; import com.google.common.io.CountingInputStream; import eu.europeana.cloud.service.mcs.exception.FileAlreadyExistsException; import eu.europeana.cloud.service.mcs.exception.FileNotExistsException; import eu.europeana.cloud.service.mcs.persistent.exception.SwiftConnectionException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.jclouds.blobstore.BlobStore; import org.jclouds.blobstore.ContainerNotFoundException; import org.jclouds.blobstore.domain.Blob; import org.jclouds.blobstore.domain.BlobBuilder; import org.jclouds.blobstore.options.GetOptions; import org.jclouds.io.Payload; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; /** * Provides DAO operations for Openstack Swift. */ @Repository public class SwiftContentDAO implements ContentDAO { @Autowired private SwiftConnectionProvider connectionProvider; /** * Puts given content to storage under given fileName. Counts and returns * content length and md5 checksum from given data. * * @param fileName * name of the file * @param data * content of file to be saved * @return md5 and content length * @throws IOException * if an I/O error occurs */ @Override public PutResult putContent(String fileName, InputStream data) throws IOException, ContainerNotFoundException { BlobStore blobStore = connectionProvider.getBlobStore(); String container = connectionProvider.getContainer(); CountingInputStream countingInputStream = new CountingInputStream(data); DigestInputStream md5DigestInputStream = md5InputStream(countingInputStream); BlobBuilder builder = blobStore.blobBuilder(fileName); builder = builder.name(fileName); builder = builder.payload(md5DigestInputStream); Blob blob = builder.build(); blobStore.putBlob(container, blob); String md5 = BaseEncoding.base16().lowerCase().encode(md5DigestInputStream.getMessageDigest().digest()); Long contentLength = countingInputStream.getCount(); return new PutResult(md5, contentLength); } /** * Retrieves content of file from storage. Can retrieve range of bytes of * the file. * * @param fileName * name of the file to retrieve * @param start * first offset included in the response. If equal to -1, * ignored. * @param end * last offset included in the response (inclusive). If equal to * -1, ignored. * @param os * outputstream the content is written to * @throws IOException * if an I/O error occurs * @throws FileNotExistsException * if object does not exist in the storage */ @Override public void getContent(String fileName, long start, long end, OutputStream os) throws IOException, FileNotExistsException, ContainerNotFoundException { BlobStore blobStore = connectionProvider.getBlobStore(); String container = connectionProvider.getContainer(); if (!blobStore.blobExists(connectionProvider.getContainer(), fileName)) { throw new FileNotExistsException(String.format("File %s not exists", fileName)); } GetOptions options = GetOptions.NONE; if (start > -1 && end > -1) { options = new GetOptions().range(start, end); } else if (start > -1 && end == -1) { options = new GetOptions().startAt(start); } else if (start == -1 && end > -1) { options = new GetOptions().range(0, end); } Payload payload = blobStore.getBlob(container, fileName, options).getPayload(); if (payload != null) { ByteStreams.copy(payload.getInput(), os); } } /** * Copies content of one storage object to another. * * @param sourceObjectId * name of the source storage object * @param trgObjectId * name of the target storage object * @throws FileNotExistsException * if source object does not exist in the storage */ @Override public void copyContent(String sourceObjectId, String trgObjectId) throws FileNotExistsException, FileAlreadyExistsException { BlobStore blobStore = connectionProvider.getBlobStore(); String container = connectionProvider.getContainer(); if (!blobStore.blobExists(connectionProvider.getContainer(), sourceObjectId)) { throw new FileNotExistsException(String.format("File %s not exists", sourceObjectId)); } if (blobStore.blobExists(connectionProvider.getContainer(), trgObjectId)) { throw new FileAlreadyExistsException(String.format("Target file %s already exists", trgObjectId)); } Blob blob = blobStore.getBlob(container, sourceObjectId); Blob newBlob = blobStore.blobBuilder(trgObjectId).name(trgObjectId).payload(blob.getPayload()).build(); blobStore.putBlob(container, newBlob); } /** * Deletes storage object identified by fileName. * * @param fileName * name of the object to be deleted * @throws FileNotExistsException * if object does not exist in the storage */ @Override public void deleteContent(String fileName) throws FileNotExistsException { BlobStore blobStore = connectionProvider.getBlobStore(); String container = connectionProvider.getContainer(); if (!blobStore.blobExists(connectionProvider.getContainer(), fileName)) { throw new FileNotExistsException(String.format("File %s not exists", fileName)); } blobStore.removeBlob(container, fileName); } private DigestInputStream md5InputStream(InputStream is) { try { MessageDigest md = MessageDigest.getInstance("MD5"); return new DigestInputStream(is, md); } catch (NoSuchAlgorithmException ex) { throw new AssertionError("Cannot get instance of MD5 but such algorithm should be provided", ex); } } }